use hirun::net::*;
use hirun::runtime;

mod http;
use http::*;

async fn server_conn(conn: Fd, size: usize) {
    conn.set_linger(Some(Duration::default()));
    let mut aio = AioFd::new(&conn);
    loop {
        let mut req = HttpReq::new();
        if let Err(_) = req.read(&mut aio).await {
            break;
        }

        let rsp = HttpRsp::new(size);
        if let Err(_) = rsp.write(&mut aio).await {
            break;
        }
    }
    let _ = conn.shutdown(SHUT_RDWR);
}

async fn server(addr: &SocketAddr, size: usize) {
    let server = match Fd::tcp_server(addr) {
        Ok(server) => server,
        Err(_e) => {
            println!("tcp_server fail: {_e}");
            return;
        }
    };
    let mut aio = AioFd::new(&server);
    loop {
        match aio.accept().await {
            Ok((conn, _)) => {
                let _ = runtime::task::spawn(server_conn(conn, size)).await;
            }
            Err(_e) => {}
        };
    }
}

use std::time::*;

fn help() {
    println!("{} options", hictor::program_invocation_name());
    println!("options:");
    println!("--addr | -a: listening addr, format is port | ip:port");
    println!("--size | -s: body_length, default is 1024");
    println!("--cache: max task-cache size, default is 0");
    println!("--threads | -t: default is 0");
    println!("--help | -h: print help message");
}

fn main() {
    let opts = hiopt::options!["addr:", "a:", "size:", "s:", "cache:", "threads:", "t:",  "help", "h"];
    let args = unsafe { hiopt::raw_args_from_i8(hictor::args()) };
    let mut addr = SocketAddr::inet_from("2000").unwrap();
    let mut size = 1024_usize;
    let mut cache = 0_usize;
    let mut nth = 0_usize;
    for opt in opts.opt_iter(&args[1..]) {
        let (optidx, optval) =  opt.unwrap();
        match optidx {
            0..=1 => addr = SocketAddr::inet_from(optval.unwrap()).unwrap(),
            2..=3 => size = optval.unwrap().parse().unwrap(),
            4 => cache = optval.unwrap().parse().unwrap(),
            5..=6 => nth = optval.unwrap().parse().unwrap(),
            7..=8 => return help(),
            _ => unreachable!(),
        }
    }
    let _ = runtime::Builder::new()
        .name("http")
        .max_cache(cache)
        .nth(nth)
        .build();
    let _ = runtime::block_on_with(server(&addr, size), runtime::Attr::new().priority(1));
}
